import { Component, cls, type StyledProps, setCSS } from '@knno/jsx';
import { DATA } from '../symbol';
import { foldableStyle } from './foldable.css';

export interface FoldableProp extends StyledProps<Foldable> {
	expanded?: boolean;
	aniDuration?: number;
	onFoldChanged?: (this: Foldable, expanded: boolean) => void;
}

export class Foldable extends Component<FoldableProp, Foldable, HTMLDivElement> {
	protected render(): Node {
		return (
			<div class={cls(this.props.class, foldableStyle)} style={this.props.style}>
				{this.props.children}
			</div>
		);
	}
	[DATA]: {
		timer?: number;
	} = {};
	constructor(props: FoldableProp) {
		super(props);
		setCSS(this.el, {
			transition: `height ${this.aniDuration}ms`,
			height: this.expanded ? '' : '0px',
		});
		this.el.classList.toggle('shrinked', !this.expanded);
	}

	private expand(noAni = false) {
		clearTimeout(this[DATA].timer);
		const currentHeight = this.el.offsetHeight;
		this.el.style.height = '';
		this.el.classList.remove('shrinked');
		const realHeight = this.el.offsetHeight;
		this.el.style.height = currentHeight + 'px';
		if (noAni) {
			this.el.style.height = '';
		} else {
			setTimeout(() => {
				clearTimeout(this[DATA].timer);
				this.el.style.height = realHeight + 'px';
				this[DATA].timer = setTimeout(() => {
					this.el.style.height = '';
					this.props.onFoldChanged?.call(this, true);
				}, this.aniDuration);
			}, 32);
		}
	}

	private shrink(noAni = false) {
		clearTimeout(this[DATA].timer);
		const currentHeight = this.el.offsetHeight;
		this.el.style.height = currentHeight + 'px';
		if (noAni) {
			this.el.style.height = '0px';
		} else {
			setTimeout(() => {
				this.el.style.height = '0px';
				this[DATA].timer = setTimeout(() => {
					this.el.classList.add('shrinked');
					this.props.onFoldChanged?.call(this, false);
				}, this.aniDuration);
			}, 32);
		}
	}

	get expanded(): boolean {
		return this.props.expanded ?? false;
	}
	setExpanded(value?: boolean, noAni = false): this {
		this.props.expanded = value;
		if (value) {
			this.expand(noAni);
		} else {
			this.shrink(noAni);
		}
		return this;
	}

	get aniDuration(): number {
		return this.props.aniDuration ?? 300;
	}
	setAniDuration(value: number): number | this {
		this.props.aniDuration = value;
		this.el.style.transition = `height ${value}ms`;
		return this;
	}

	toggle() {
		this.setExpanded(!this.expanded);
	}

	onFoldChanged(handler: ((this: Foldable, expanded: boolean) => void) | undefined): this {
		this.props.onFoldChanged = handler;
		return this;
	}
}
